package pta;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

import javafx.util.Pair;

import soot.Local;
import soot.SootField;

class AssignConstraint {
	Local from, to;

	AssignConstraint(Local from, Local to) {
		this.from = from;
		this.to = to;
	}
}

class NewConstraint {
	Local to;
	int allocId;

	NewConstraint(int allocId, Local to) {
		this.allocId = allocId;
		this.to = to;
	}
}

class FieldStoreConstraint {
	Local from, to;
	SootField tofield;

	FieldStoreConstraint(Local from, Local to, SootField tofield) {
		this.from = from;
		this.to = to;
		this.tofield = tofield;
	}
}

class FieldLoadConstraint {
	Local from, to;
	SootField fromfield;

	FieldLoadConstraint(Local from, SootField fromfield, Local to) {
		this.from = from;
		this.to = to;
		this.fromfield = fromfield;
	}
}

class Fourth extends Pair<Integer, SootField> {
	Fourth(Integer id, SootField f) {
		super(id, f);
	}
}

public class Anderson {
	private List<AssignConstraint> assignConstraintList = new ArrayList<AssignConstraint>();
	private List<NewConstraint> newConstraintList = new ArrayList<NewConstraint>();
	private List<FieldStoreConstraint> fieldStoreConstraintList = new ArrayList<FieldStoreConstraint>();
	private List<FieldLoadConstraint> fieldLoadConstraintList = new ArrayList<FieldLoadConstraint>();
	Map<Local, TreeSet<Integer>> pts = new HashMap<Local, TreeSet<Integer>>();
	Map<Fourth, TreeSet<Integer>> fld = new HashMap<Fourth, TreeSet<Integer>>();

	void addAssignConstraint(Local from, Local to) {
		// System.out.println("Assign : " + "from: " + from + "," + from.hashCode() + "
		// to: " + to + "," + to.hashCode());
		assignConstraintList.add(new AssignConstraint(from, to));
	}

	void addNewConstraint(int alloc, Local to) {
		// System.out.println("New : " + alloc + " " + to.toString());
		newConstraintList.add(new NewConstraint(alloc, to));
	}

	void addFieldStoreConstraint(Local from, Local to, SootField tofield) {
		fieldStoreConstraintList.add(new FieldStoreConstraint(from, to, tofield));
	}

	void addFieldLoadConstraint(Local from, SootField fromfield, Local to) {
		fieldLoadConstraintList.add(new FieldLoadConstraint(from, fromfield, to));
	}

	void run() {
		// System.out.println("Anderson Run...");
		for (NewConstraint nc : newConstraintList) {
			if (!pts.containsKey(nc.to)) {
				pts.put(nc.to, new TreeSet<Integer>());

			}
			pts.get(nc.to).add(nc.allocId);
		}
		for (boolean flag = true; flag;) {
			flag = false;
			for (AssignConstraint ac : assignConstraintList) {
				// System.out.println("Try Assign prop:" + ac.from.toString() + " " +
				// ac.to.toString());
				if (!pts.containsKey(ac.from)) {
					continue;
				}
				if (!pts.containsKey(ac.to)) {
					pts.put(ac.to, new TreeSet<Integer>());
				}

				if (pts.get(ac.to).addAll(pts.get(ac.from))) {
					flag = true;
				}
			}
			for (FieldStoreConstraint fsc : fieldStoreConstraintList) {
				if (!pts.containsKey(fsc.from) || !pts.containsKey(fsc.to)) {
					continue;
				}
				for (Integer id : pts.get(fsc.to)) {
					Fourth fto = new Fourth(id, fsc.tofield);
					if (!fld.containsKey(fto)) {
						fld.put(fto, new TreeSet<Integer>());
					}
					if (fld.get(fto).addAll(pts.get(fsc.from))) {
						flag = true;
					}
				}

			}

			for (FieldLoadConstraint fsc : fieldLoadConstraintList) {
				if (!pts.containsKey(fsc.from))
					continue;
				for (Integer id : pts.get(fsc.from)) {
					Fourth ffrom = new Fourth(id, fsc.fromfield);
					if (!fld.containsKey(ffrom))
						continue;
					if (!pts.containsKey(fsc.to))
						pts.put(fsc.to, new TreeSet<Integer>());
					if (pts.get(fsc.to).addAll(fld.get(ffrom)))
						flag = true;
				}
			}
		}
	}

	TreeSet<Integer> getPointsToSet(Local value) {
		// if (pts.get(value) == null)
		// System.out.println("get from " + value.toString() + " is null.");
		return pts.get(value);
	}

}
